package engine

import (
	"errors"
	"os"
	"path/filepath"
	"time"

	"engine/db"
	"engine/eng_cfg"
	"engine/internal/misc"
	"engine/internal/node"
	"engine/log"
	"engine/stream/net"
	"engine/stream/net/http"

	"gitee.com/ameise84/e3dds"
	"gitee.com/ameise84/e3log"
	"gitee.com/ameise84/e3utils/safe"
	"gitee.com/ameise84/e3utils/sys"
	"gitee.com/ameise84/e3utils/utils"
	"github.com/gin-gonic/gin"
	"github.com/spf13/viper"
	"golang.org/x/time/rate"
)

var (
	_gNodeName               string
	_gNetService             e3dds.NetService
	_gNetMsgTransformHandler NetMsgTransformHandler
)

func SetNetMsgTransformHandler(hand NetMsgTransformHandler) {
	_gNetMsgTransformHandler = hand
}

func RegisterNetService(serviceName string, fact e3dds.ActorFactory) (netService e3dds.NetService, err error) {
	if _gNetService != nil {
		return nil, errors.New("net service already registered")
	}
	_gNetService, err = e3dds.RegisterNetService(serviceName, fact, 8)
	return _gNetService, nil
}

func RegisterRpcService(serviceName string, fact e3dds.ActorFactory, opts ...*e3dds.ServiceOptions) (e3dds.RpcService, error) {
	var tsOpts *e3dds.ServiceOptions
	if len(opts) > 0 {
		tsOpts = opts[0]
	} else {
		tsOpts = e3dds.DefaultServiceOptions()
	}
	return e3dds.RegisterRpcService(serviceName, fact, tsOpts)
}

func InitConfig(cfgPath string) error {
	setDefaultCfg()
	viper.SetConfigType("yaml")
	viper.AddConfigPath(cfgPath)

	viper.SetConfigName("config_node")
	err := viper.ReadInConfig()
	if err != nil {
		return err
	}

	viper.SetConfigName("config_base")
	err = viper.MergeInConfig()
	if err != nil {
		return err
	}
	return checkCfg()
}

func Start(cfgPath string, onStart func() error) (err error) {
	misc.CheckDieLock()
	_gNodeName = viper.GetString(eng_cfg.AppNode)
	if err = checkCfg(); err != nil {
		return err
	}
	baseLog := log.GetLogger()
	if err = baseLog.SetLevelByName(viper.GetString(eng_cfg.LogLevel)); err != nil {
		return
	}

	limitFill := viper.GetFloat64(eng_cfg.LogLimitFill)
	var limiter *e3log.LimiterLevel
	if limitFill != 0 {
		limitBucket := viper.GetInt(eng_cfg.LogLimitBucket)
		limiter = e3log.NewLimiterLevel()
		baseLimit := rate.NewLimiter(rate.Limit(limitFill), limitBucket)
		warnLimit := rate.NewLimiter(rate.Limit(limitFill), limitBucket)
		limiter.SetLevelLimit(baseLimit, e3log.LevelTrace, e3log.LevelDebug, e3log.LevelInfo)
		limiter.SetLevelLimit(warnLimit, e3log.LevelWarn)
		baseLog.SetLimiter(limiter)
	}

	if size := viper.GetInt(eng_cfg.LogFileSize); size > 0 {
		appSubDir := viper.GetString(eng_cfg.LogFileAppPath)
		basePath := utils.GetCurrentExeDir()
		logFilePath := filepath.Join(basePath, appSubDir, _gNodeName+".log")
		baseLog.Info("log file").Str("path", logFilePath).Println()

		logFileCfg := e3log.DefaultFileWriterConfig()
		logFileCfg.FileName = logFilePath
		var logFile e3log.Writer
		logFile, err = e3log.NewFileWriter(logFileCfg)
		if err != nil {
			return
		}
		baseLog.AddWriter(logFile)
		baseLog.SetFatalExitFunc(sys.Exit, 1)

		//启动独立日志
		dbSubDir := viper.GetString(eng_cfg.LogFileDBPath)
		if dbSubDir != appSubDir && dbSubDir != "" {
			dbFilePath := filepath.Join(basePath, dbSubDir, _gNodeName+".db.log")
			dbFileCfg := e3log.DefaultFileWriterConfig()
			logFileCfg.FileName = dbFilePath
			var dbFile e3log.Writer
			dbFile, err = e3log.NewFileWriter(dbFileCfg)
			if err != nil {
				return
			}
			_gDBLog := e3log.NewLogger(e3log.NewConsoleWriter(os.Stdout), dbFile)
			_gDBLog.SetFatalExitFunc(sys.Exit, -2)
			_gDBLog.SetLimiter(limiter)
			db.SetLogger(_gDBLog)
		} else {
			db.SetLogger(baseLog)
		}
	}

	err = node.Start()
	if err != nil {
		return
	}
	defer func() {
		if err != nil {
			node.Stop()
		}
	}()
	if err = net.Start(_gNetService, _gNetMsgTransformHandler); err != nil {
		return err
	}
	defer func() {
		if err != nil {
			net.Stop(_gNetService)
		}
	}()
	if onStart == nil {
		return
	}
	return onStart()
}

func Stop(onStop func()) {
	safe.Func(nil, "engine stop", func() {
		net.Stop(_gNetService)
		node.Stop()
	})
	log.GetLogger().Warn("服务器已关闭").Println()
	db.GetLogger().Wait()
	log.GetLogger().Wait()
	if onStop != nil {
		onStop()
	}
	time.Sleep(2 * time.Second)
}

func RegisterHttpPostRouter(netService e3dds.NetService, routerPath string, hand e3dds.ProtocolHandler, hooks ...gin.HandlerFunc) {
	netService.RegisterProtocolHandlerWithName(routerPath, hand)
	http.Post(routerPath, hooks...)
}

func RegisterHttpGetRouter(netService e3dds.NetService, routerPath string, h e3dds.ProtocolHandler, hooks ...gin.HandlerFunc) {
	netService.RegisterProtocolHandlerWithName(routerPath, h)
	http.Get(routerPath, hooks...)
}

func RegisterHttpDelRouter(netService e3dds.NetService, routerPath string, h e3dds.ProtocolHandler, hooks ...gin.HandlerFunc) {
	netService.RegisterProtocolHandlerWithName(routerPath, h)
	http.Del(routerPath, hooks...)
}

func GetNodeName() string {
	return _gNodeName
}

//func ExistService(name string) bool {
//	return mid.MsgRouterHandler.ExistService(name, "")
//}
//
//func ExistServiceFix(name string, session string) bool {
//	return mid.MsgRouterHandler.ExistService(name, session)
//}

func GetDeployment() string {
	return misc.GetDeployment()
}
